#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/shm.h>
#include <iostream>
#include <cstring>

#define PATHNAME "/home/lyh/learning/code_in_linux/11.Shm"
#define PROJ_ID 0x666
#define SHMSIZE 4096
enum shmCharacter
{
    shmCreater,
    shmUser
};

class Shm
{
private:
    void *AthShm()
    {
        if (_shmadd != nullptr)
            shmdt(_shmadd);
        _shmadd = shmat(_shmid, nullptr, 0);
        std::cout << "address attached" << std::endl;
        return _shmadd;
    }

public:
    Shm(std::string pathname, int projid, int shmsize, shmCharacter who)
        : _key(0), _shmid(0), _pathname(pathname), _projid(projid), _shmsize(shmsize), _cha(who), _shmadd(nullptr)
    {
        _key = ftok(_pathname.c_str(), _projid);
        if (_cha == shmCreater)
        {
            _shmid = shmget(_key, _shmsize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid < 0)
            {
                perror("shmget");
                exit(-1);
            }
            std::cout << "creater done" << std::endl;
            std::cout << "key: " << ToHex(_key) << std::endl;
            std::cout << "shmid: " << _shmid << std::endl;
        }
        else
        {
            _shmid = shmget(_key, _shmsize, IPC_CREAT | 0666);
            if (_shmid < 0)
            {
                perror("shmget");
                exit(-1);
            }
            std::cout << "user done" << std::endl;
            std::cout << "key: " << ToHex(_key) << std::endl;
            std::cout << "shmid: " << _shmid << std::endl;
        }

        AthShm();
    }

    ~Shm()
    {
        if (shmCreater == _cha)
        {
            shmctl(_shmid, IPC_RMID, nullptr);
        }
        std::cout << "shm removed" << std::endl;
    }

    void Zero()
    {
        memset(_shmadd, 0, _shmsize);
    }

    void *Add()
    {
        return _shmadd;
    }

    std::string ToHex(int key)
    {
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }

    int Size()
    {
        return _shmsize;
    }

private:
    key_t _key;
    int _shmid;
    std::string _pathname;
    int _projid;
    int _shmsize;
    shmCharacter _cha;
    void *_shmadd;
};